﻿#region Copyright and License Notice
// Copyright (C)2010-2016 - Rob Levine and other contributors
// http://configgen.codeplex.com
// 
// This file is part of ConfigGen.
// 
// ConfigGen is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// ConfigGen is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License and 
// the GNU Lesser General Public License along with ConfigGen.  
// If not, see <http://www.gnu.org/licenses/>
#endregion

using System;
using System.IO;
using System.Linq;
using ConfigGen.Core.ConfigurationSettings;
using ConfigGen.Core.TemplateLoader;
using ConfigGen.Core.TemplateProcessing;
using ConfigGen.Utilities.Extensions.System.Collections.Generic;

namespace ConfigGen.Core
{
    /// <summary>
    /// Class responsible for orchestrating the generation of a single configuration file from a template and settings collection.
    /// </summary>
    public class SingleFileGenerationController : ISingleFileGenerationController
    {
        private readonly ITemplateProcessor _templateProcessor;
        private readonly IOutputWriter<FileOutputWriterResults> _outputWriter;
        private readonly ISingleFileGenerator _singleFileGenerator;

        /// <summary>
        /// Initializes a new instance of the <see cref="SingleFileGenerationController"/> class.
        /// </summary>
        /// <param name="templateProcessor">The template processor.</param>
        /// <param name="singleFileGenerator">The single file generator.</param>
        /// <param name="outputWriter">The output writer.</param>
        /// <exception cref="ArgumentNullException">Thrown if any of the arguments are null.</exception>
        public SingleFileGenerationController(ITemplateProcessor templateProcessor,  ISingleFileGenerator singleFileGenerator, IOutputWriter<FileOutputWriterResults> outputWriter)
        {
            if (templateProcessor == null) throw new ArgumentNullException("templateProcessor");
            if (singleFileGenerator == null) throw new ArgumentNullException("singleFileGenerator");
            if (outputWriter == null) throw new ArgumentNullException("outputWriter");
            _templateProcessor = templateProcessor;
            _singleFileGenerator = singleFileGenerator;
            _outputWriter = outputWriter;
        }

        /// <summary>
        /// Generates a single configuration file from the supplied template loaded, machine settings and
        /// preferences.
        /// </summary>
        /// <param name="templateLoader">Template loader</param>
        /// <param name="currentMachine">Settings for the current machine</param>
        /// <param name="preferences">Preferences for each</param>
        /// <returns>Results from the generation.</returns>
        /// <exception cref="ArgumentNullException">Thrown if any of the parameters are null</exception>
        public SingleFileGenerationResults GenerateFile(
            ITemplateLoader templateLoader, 
            MachineConfigurationSettings currentMachine, 
            Preferences preferences)
        {
            if (templateLoader == null) throw new ArgumentNullException("templateLoader");
            if (currentMachine == null) throw new ArgumentNullException("currentMachine");
            if (preferences == null) throw new ArgumentNullException("preferences");

            var templatePreferences = templateLoader.TemplatePreferences;

            var tokensForMachine = from token in currentMachine.ConfigurationSettings
                                   where token.Value != null
                                   select token.Token;

            var tokenCounts = new TokenCounts();

            tokenCounts.TokenUseCounts.AddKeysToCountDictionary(tokensForMachine);

            using (var processedTemplateStream = new MemoryStream())
            using (var targetConfigStream = new MemoryStream())
            {
                _singleFileGenerator.Initialise(
                    currentMachine, 
                    tokenCounts,
                    templatePreferences);

                _singleFileGenerator.ProcessTemplate(
                    templateLoader.GetTemplateStream(),
                    _templateProcessor,
                    processedTemplateStream);

                _singleFileGenerator.ReplaceTokens(
                    processedTemplateStream,
                    targetConfigStream);

                var fileWriteResults = _outputWriter.WriteFile(
                    currentMachine,
                    targetConfigStream,
                    preferences,
                    templatePreferences);

                return new SingleFileGenerationResults
                {
                    MachineName = currentMachine.MachineName,
                    GeneratedFile = fileWriteResults.GeneratedFile,
                    NewFileMd5Hash = fileWriteResults.NewMd5,
                    OldFileMd5Hash = fileWriteResults.OldMd5,
                    TokenUseCounts = tokenCounts.TokenUseCounts,
                    UnrecognisedTokenCounts = tokenCounts.UnrecognisedTokenCounts,
                };
            }
        }
    }
}
